package com.hxc.sercurity.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hxc.sercurity.configu.projo.JwtUtil;
import com.hxc.sercurity.configu.projo.User;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 韩先楚
 */
@Component
@Slf4j
public class JwtAuthTokenFilter extends OncePerRequestFilter {
    /**
     * 这个自定义过滤器要将其安放在PasswordAuthenticationFilter之前
     * 当一个请求过来获取其jwt 然后从Redis中获取用户信息
     * 然后将其放到SecurityContextHolder中
     * SecurityContextHolder是PasswordAuthenticationFilte过滤器的最后一步
     * 这样就相当于直接跳过了PasswordAuthenticationFilte 不用查数据库来验证用户了
     * 直接调用后面的过滤器链
     * 后面的过滤器链会从SecurityContextHolder中获取信息
     * 当将用户信息放到SecurityContextHolder中 就会到后面的认证的过滤器链
     * 然后进行认证 如果SecurityContextHolder中没有用户信息就会认证失败
     * 这个过滤器是十分重要的
     */
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
       //获取Token
        String jwt = request.getHeader("Token");
        if (jwt == null) {
            filterChain.doFilter(request, response);
            return;
        }
        //解析jwt
        JwtUtil hxc = new JwtUtil("hxc");
        Claims claims = hxc.extractClaims(jwt);
        if (claims == null)
        {
            filterChain.doFilter(request,response);
            log.info("jwt错误");
            //jwt错误
            return;
        }
        Integer i = claims.get("userId", Integer.class);
        //从Redis中获取当前用户信息
        Object o = redisTemplate.opsForHash().get("loginUser", "user" + i);
        if (o == null) {
            filterChain.doFilter(request,response);
            log.info("redis中的用户不存在");
            return;
        }
        String user = (String) o;
        ObjectMapper mapper = new ObjectMapper();
        User read = mapper.readValue(user, User.class);

        //TODO 权限信息封装
        String role = read.getRole();
        List<String> list =new ArrayList<>();
        list.add(role);
        List<SimpleGrantedAuthority> collect = list.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        //将用户信息放到SecurityContextHolder中
        /**
         * 当你使用 SecurityContextHolder.getContext().setAuthentication() 方法设置一个新的认证对象时，
         * 当前线程的认证信息将被更新。这意味着，如果这个方法在 UsernamePasswordAuthenticationFilter
         * 之前被调用，那么 UsernamePasswordAuthenticationFilter 将不会处理这个请求，
         * 因为它会认为当前线程已经有一个有效的认证对象。
         */
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(read, null, collect));
        filterChain.doFilter(request,response);
    }


}
